/*
 * This file is part of FinVis.
 *
 * FinVis is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FinVis is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with FinVis.  If not, see <http://www.gnu.org/licenses/>
 *
 * @author Stephen Rudolph
 */

#ifndef FIN_PORTFOLIO_H
#define FIN_PORTFOLIO_H

#include <QObject>

#include <map>
#include <vector>
#include "Common.h"
#include "FinInvestment.h"

/**
 * The FinPortfolio class contains a set of active investments and computes the effect of covariate
 * risk on the entire set.
 * Note: for investment arrays, indices start at the outside (most recent) 0 and move in (less recent)
 */
class FinPortfolio : public QObject {
    Q_OBJECT
signals:
    void selectionChanged();
    void portfolioDataChanged();
public slots:
    void removeSelectedInvestment();
    void addAndSelectInvestment();
    void setSelectedInvestment(FinInvestment investment);
    void setSelectedID(unsigned int investmentID);
public:
    FinPortfolio();
    ~FinPortfolio();

    //Return # of investments
    unsigned int investmentCount();

    //Get investment map
    std::map<unsigned int, FinInvestment> investments();

    //Add a new investment, return the index of the investment
    unsigned int addInvestment();
    unsigned int addInvestment(FinInvestment *investment);
    unsigned int addAndSelectInvestment(FinInvestment *investment);

    //Remove an investment
    void removeAllInvestments();
    void removeInvestment(unsigned int id);

    //Get an existing investment at the specified vector index
    FinInvestment* investmentForID(unsigned int investmentID);
    FinInvestment* selectedInvestment();

    //Modify an existing investment
    void setInvestment(unsigned int investmentID, FinInvestment *investment);

    //Modify/access selectedIndex
    unsigned int selectedID();
    unsigned int nextID();
    unsigned int previousID();

    //Modify base liquid assests for this scenario
    void setScenarioLiquidAssets(int scenarioLiquidAssetsArray[MAX_YEARS]);

    int liquidAssets(unsigned int yearIndex);

    //Get calculated investment values
    int expectedWealth(unsigned int yearIndex);
    int optimisticWealth(unsigned int yearIndex);
    int pessimisticWealth(unsigned int yearIndex);
    int optimisticWealth(unsigned int yearIndex, unsigned int investmentID);
    int pessimisticWealth(unsigned int yearIndex, unsigned int investmentID);
    double getAggregatePercentRisk();
    double getAggregatePercentReturn();

    //Return investment's information
    int principalForInvestment(unsigned int investmentID);
    int startYearForInvestment(unsigned int investmentID);
    int durationForInvestment(unsigned int investmentID);
    int investmentOptionVectorIndexForInvestment(unsigned int investmentID);

    bool changingSelectedInvestmentRequiresDebt(FinInvestment proposed);
    bool changingInvestmentRequiresDebt(int investmentID, FinInvestment proposed);
private:
    //Recalculate all values
    void recalculatePortfolio();

    std::vector<int> investmentsStartingInYear(int investmentYear);

    //MPT Algorithms
    int calculateAggregatePrincipal(unsigned int investmentCount, int *investmentPrincipals);
    double calculateAggregateReturn(unsigned int investmentCount, double *investmentReturns,
                                    double *investmentWeights);
    double calculateAggregateRisk(unsigned int investmentCount, double *investmentWeights,
                                  double *investmentRisks, double **investmentCorrelations);
    double calculateOptimisticInterest(int investmentPrincipal, double investmentRisk,
                                       double investmentReturn, int investmentDuration,
                                       bool simpleInterest);
    double calculateOptimisticInterest(FinInvestment investment);
    double calculatePessimisticInterest(int investmentPrincipal, double investmentRisk,
                                        double investmentReturn, int investmentDuration,
                                        bool simpleInterest);
    double calculatePessimisticInterest(FinInvestment investment);
    double* matrixVectorMultiply(unsigned int length, double *vector, double **matrix);
    double l1Norm(unsigned int length, double *vector);
    double* reallocateInterest(unsigned int investmentCount, double aggregateInterest,
                               double *investmentInterests, double **investmentCorrelations);
    /*
     *Keys will be in increasing order, allowing for a unique and persistent
     *sort pattern
     */
    std::map<unsigned int, FinInvestment> m_investments;
    unsigned int m_currentlySelectedID;
    unsigned int m_maxInvestmentCount;
    unsigned int m_lastInvestmentID;
    bool m_hypothetical;
    double m_liquidAssetsArray[MAX_YEARS + 1], m_scenarioLiquidAssetsArray[MAX_YEARS + 1],
        m_expectedWealthArray[MAX_YEARS + 1], **m_optimisticWealthArray, **m_pessimisticWealthArray;
    double m_aggregatePercentRisk, m_aggregatePercentReturn;
};

#endif
